package com.wzb.mybatisplus.bitaction.electrumaction;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.bitcoinj.core.Address;
import org.bitcoinj.core.NetworkParameters;
import org.bitcoinj.params.MainNetParams;
import org.bitcoinj.script.Script;
import org.bitcoinj.script.ScriptBuilder;

import java.io.*;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;

public class ElectrumClient {
    private final String host;
    private final int port;
    private Socket socket;
    private BufferedReader reader;
    private PrintWriter writer;
    private final ObjectMapper mapper;
    private final AtomicInteger requestId;
    private final NetworkParameters networkParams;

    // Electrum JSON-RPC 方法常量
    private static final String METHOD_PING = "server.ping";
    private static final String METHOD_VERSION = "server.version";
    private static final String METHOD_BANNER = "server.banner";
    private static final String METHOD_BALANCE = "blockchain.scripthash.get_balance";
    private static final String METHOD_HISTORY = "blockchain.scripthash.get_history";
    private static final String METHOD_LIST_UNSPENT = "blockchain.scripthash.listunspent";
    private static final String METHOD_GET_TRANSACTION = "blockchain.transaction.get";
    private static final String METHOD_BROADCAST = "blockchain.transaction.broadcast";
    private static final String METHOD_HEADER = "blockchain.headers.subscribe";
    private static final String METHOD_FEE = "blockchain.estimatefee";

    public ElectrumClient(String host, int port) {
        this.host = host;
        this.port = port;
        this.mapper = new ObjectMapper();
        this.requestId = new AtomicInteger(0);
        this.networkParams = MainNetParams.get(); // 或者 TestNet3Params.get() 用于测试网
    }

    /**
     * 连接到Electrum服务器
     */
    public void connect() throws IOException {
        socket = new Socket(host, port);
        reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
        writer = new PrintWriter(socket.getOutputStream(), true);
        System.out.println("成功连接到Electrum服务器: " + host + ":" + port);

        // 测试连接
        String version = getServerVersion();
        System.out.println("服务器版本: " + version);
    }

    /**
     * 断开连接
     */
    public void disconnect() {
        try {
            if (reader != null) reader.close();
            if (writer != null) writer.close();
            if (socket != null) socket.close();
            System.out.println("已断开与Electrum服务器的连接");
        } catch (IOException e) {
            System.err.println("断开连接时出错: " + e.getMessage());
        }
    }

    /**
     * 发送JSON-RPC请求
     */
    private JsonNode sendRequest(String method, Object... params) throws IOException {
        Map<String, Object> request = new HashMap<>();
        int id = requestId.incrementAndGet();
        request.put("id", id);
        request.put("method", method);
        request.put("params", params);
        request.put("jsonrpc", "2.0");

        String requestJson = mapper.writeValueAsString(request);

        synchronized (this) {
            writer.println(requestJson);
            writer.flush();

            String responseLine = reader.readLine();
            if (responseLine == null) {
                throw new IOException("服务器关闭了连接");
            }

            JsonNode responseNode = mapper.readTree(responseLine);

            // 检查错误
            if (responseNode.has("error") && !responseNode.get("error").isNull()) {
                String errorMsg = responseNode.get("error").toString();
                throw new IOException("Electrum RPC错误: " + errorMsg);
            }

            return responseNode;
        }
    }

    /**
     * 获取服务器版本
     */
    public String getServerVersion() throws IOException {
        JsonNode response = sendRequest(METHOD_VERSION, "java-electrum-client", "1.4");
        return response.get("result").toString();
    }

    /**
     * 服务器ping测试
     */
    public boolean ping() throws IOException {
        try {
            sendRequest(METHOD_PING);
            return true;
        } catch (IOException e) {
            return false;
        }
    }

    /**
     * 获取服务器横幅信息
     */
    public String getBanner() throws IOException {
        JsonNode response = sendRequest(METHOD_BANNER);
        return response.get("result").asText();
    }

    /**
     * 将比特币地址转换为脚本哈希
     */
    public String addressToScriptHash(String addressStr) {
        try {
            Address address = Address.fromString(networkParams, addressStr);
            Script script = ScriptBuilder.createOutputScript(address);
            byte[] scriptBytes = script.getProgram();
            byte[] hash = sha256(scriptBytes);

            // 反转字节顺序
            StringBuilder sb = new StringBuilder();
            for (int i = hash.length - 1; i >= 0; i--) {
                sb.append(String.format("%02x", hash[i]));
            }
            return sb.toString();
        } catch (Exception e) {
            throw new RuntimeException("地址转换失败: " + e.getMessage());
        }
    }

    /**
     * SHA256哈希计算
     */
    private byte[] sha256(byte[] input) {
        try {
            java.security.MessageDigest digest = java.security.MessageDigest.getInstance("SHA-256");
            return digest.digest(input);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 获取地址余额
     */
    public BalanceInfo getAddressBalance(String address) throws IOException {
        String scriptHash = addressToScriptHash(address);
        JsonNode response = sendRequest(METHOD_BALANCE, scriptHash);
        JsonNode result = response.get("result");

        long confirmed = result.get("confirmed").asLong();
        long unconfirmed = result.get("unconfirmed").asLong();

        return new BalanceInfo(confirmed, unconfirmed);
    }

    /**
     * 获取地址交易历史
     */
    public JsonNode getAddressHistory(String address) throws IOException {
        String scriptHash = addressToScriptHash(address);
        JsonNode response = sendRequest(METHOD_HISTORY, scriptHash);
        return response.get("result");
    }

    /**
     * 获取未花费输出
     */
    public JsonNode getUnspentOutputs(String address) throws IOException {
        String scriptHash = addressToScriptHash(address);
        JsonNode response = sendRequest(METHOD_LIST_UNSPENT, scriptHash);
        return response.get("result");
    }

    /**
     * 获取交易详情
     */
    public String getTransaction(String txid) throws IOException {
        JsonNode response = sendRequest(METHOD_GET_TRANSACTION, txid, true);
        return response.get("result").asText();
    }

    /**
     * 广播交易
     */
    public String broadcastTransaction(String rawTransaction) throws IOException {
        JsonNode response = sendRequest(METHOD_BROADCAST, rawTransaction);
        return response.get("result").asText();
    }

    /**
     * 获取区块链头信息
     */
    public JsonNode getBlockchainHeaders() throws IOException {
        JsonNode response = sendRequest(METHOD_HEADER);
        return response.get("result");
    }

    /**
     * 估算交易费用
     */
    public double estimateFee(int blocks) throws IOException {
        JsonNode response = sendRequest(METHOD_FEE, blocks);
        return response.get("result").asDouble();
    }

    /**
     * 余额信息类
     */
    public static class BalanceInfo {
        private final long confirmed;
        private final long unconfirmed;

        public BalanceInfo(long confirmed, long unconfirmed) {
            this.confirmed = confirmed;
            this.unconfirmed = unconfirmed;
        }

        public long getConfirmed() { return confirmed; }
        public long getUnconfirmed() { return unconfirmed; }
        public long getTotal() { return confirmed + unconfirmed; }

        @Override
        public String toString() {
            return String.format("余额 - 已确认: %d, 未确认: %d, 总计: %d",
                    confirmed, unconfirmed, getTotal());
        }
    }
}
